#! /usr/bin/env python3

from package.litedb import litedb
import time


def create_table_analyse(db):
    db.sql(f"drop table if exists 'analyse'")
    #db.sql(f"create table 'analyse' (id integer primary key autoincrement, task, pid, cpu, time, func, ptr, bytes)")
    db.sql(f"create table 'analyse' (id integer primary key autoincrement, task, pid integer, cpu integer, time float, func, ptr, bytes integer)")
    db.sql(f"insert into 'analyse' select * from 'trace'")
    db.sql(f"alter table 'analyse' add column flag")
    db.sql(f"update 'analyse' set flag='-'")
    db.save()

def db_init(db):
    db.open('trace.db')
    db.sql("PRAGMA read_uncommitted=TRUE") # enable dirty read
    db.sql("update 'trace' set flag0='-'")
    db.sql("drop index if exists 'ia'")
    db.sql("create index 'ia' on 'trace' (ptr, func)")
    db.save()

def db_exit(db):
    db.save()
    db.sql("PRAGMA read_uncommitted=FALSE")
    db.close()

def check_memleak(print_interval, print_end):
    print('init  ...\n', end='')
    t0 = time.time()
    db = litedb()
    db_init(db)
    t1 = time.time()
    print(f'time  {t1-t0:.2f} sec')

    count = db.sql(f"select count(func) from 'trace' where func='kmalloc'").fetchone()[0]
    print(f'check {count} alloc')

    t0 = time.time()

    i = 0
    kid = 0
    while True:
        for kid, kptr in db.sql(f"select id, ptr from 'trace' where id>{kid} and func='kmalloc'"):
            for fid, in db.sql(f"select id from 'trace' where id>{kid} and func='kfree' and ptr='{kptr}' and flag0='-'"):
                # memory free (safe point)
                db.sql(f"update 'trace' set flag0='O' where id={kid}")
                db.sql(f"update 'trace' set flag0='+' where id={fid}")
                # break inner-for
                break
            else:
                # memory leak (leak point)
                db.sql(f"update 'trace' set flag0='X' where id={kid}")
                # break outer-for
                break
            # when breaked inner-for, break outer-for
            break
        else:
            # break while
            break

        i += 1
        if i % 10000 == 0:
            print(f'\rcheck {i*100/count:.2f}% ', end='')
        if i == print_end:
            break;
    # end-while
    db_exit(db)
    t1 = time.time()
    print(f'\rtime  {t1-t0:.2f} sec')
    #print(f"\ranalyse done! kmalloc line={i}, time={t1-t0:.4f}")


def analyse_alloc_twice():
    """
    case 1:
    kmalloc ptr=A O
    kmalloc ptr=A ???
    kfree   ptr=A +

    case 2:
    kmalloc ptr=A X
    kmalloc ptr=A ???
    """
    db = litedb()
    db.open('trace.db')
    #create_table_analyse()
    db.sql(f"drop index if exists ia")
    db.sql(f"create index ia on 'trace' (ptr, func)")

    ## case 1
    print('[for case1]')
    i = 0
    t0 = time.time()
    kid = 0
    while True:
        flag = 0
        for kid, ptr in db.sql(f"select id, ptr from 'trace' where id>{kid} and func='kmalloc' and flag0='O'"):
            for fid, in db.sql(f"select id from 'trace' where id>{kid} and ptr='{ptr}' and func='kfree'"):
                for kid2, in db.sql(f"select id from 'trace' where id>{kid} and id<{fid} and ptr='{ptr}' and func='kmalloc'"):
                    res1 = db.sql(f"select * from 'trace' where id={kid}").fetchall()
                    res2 = db.sql(f"select * from 'trace' where id={kid2}").fetchall()
                    resf = db.sql(f"select * from 'trace' where id={fid}").fetchall()
                    print('\r  case1:')
                    print(f'\r  {res1}\n  {res2}\n  {resf}')
                    break
                break
            flag = 1
            break

        i += 1
        if i % 1000 == 0:
            print(f'\r{i}', end='')

        # done
        if flag == 0:
            break

    t1 = time.time()
    print(f"\r[end case1]\ntime={t1-t0:.4f}")

    ## case 2
    print()
    print('[for case2]')
    i = 0
    t0 = time.time()
    kid = 0
    while True:
        flag = 0
        for kid, ptr in db.sql(f"select id, ptr from 'trace' where id>{kid} and func='kmalloc' and flag0='X'"):
            for kid2, in db.sql(f"select id from 'trace' where id>{kid} and ptr='{ptr}' and func='kmalloc'"):
                res  = db.sql(f"select * from 'trace' where id={kid}").fetchall()
                res2 = db.sql(f"select * from 'trace' where id={kid2}").fetchall()
                print('\r  case2:')
                print(f'\r  {res}\n  {res2}')
                break
            flag = 1
            break

        i += 1
        if i % 1000 == 0:
            print(f'\r{i}', end='')

        # done
        if flag == 0:
            break

    t1 = time.time()
    print(f"\r[end case2]\ntime={t1-t0:.4f}")

def check(func=None):
    if func is None:
        check_memleak(10000, 0)
    elif func == 'alloc_twice':
        print('analysing alloc_twice ...')
        analyse_alloc_twice()
